package com.youlu.campus.web.config.aspect;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author zgz
 * @date 2019/12/21 11:20
 */
@Aspect
@Component
@Slf4j
public class UseCacheAspect {

    @Autowired(required = false)
    private StringRedisTemplate redisTemplate;

    /**
     * 切入点
     */
    @Pointcut("@annotation(com.youlu.campus.web.config.aspect.UseCache) ")
    public void entryPoint() {
    }

    @Around("entryPoint()")
    public Object cacheAround(ProceedingJoinPoint pjp) throws Throwable {
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        Object[] args = pjp.getArgs();
        String methodName = method.getName();
        String targetName = pjp.getTarget().getClass().getName();
        String key = this.getKey(targetName, methodName, args);
        String redisValue = redisTemplate.opsForValue().get(key);
        if (StringUtils.isNotBlank(redisValue)) {
            log.info("methodName {} from cache", methodName);
            return JSON.parseObject(redisValue, method.getReturnType());
        }
        UseCache useCache = method.getAnnotation(UseCache.class);
        Object result = pjp.proceed(args);
        if (result != null && useCache != null) {
            redisTemplate.opsForValue().set(key, JSON.toJSONString(result), useCache.expireTime(), useCache.timeUnit());
        }
        log.info("methodName {} from db", methodName);
        return result;
    }

    private String getKey(String targetName, String methodName, Object[] args) {
        StringBuilder builder = new StringBuilder();
        builder.append(targetName);
        builder.append(":").append(methodName);
        if (ArrayUtils.isNotEmpty(args)) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] == null) {
                    builder.append(":").append("null");
                } else {
                    builder.append(":").append(args[i].toString());
                }
            }
        }
        return DigestUtils.md5Hex(builder.toString());
    }

}
